﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.IO;
using umbraco.cs.fixer.Helpers;

namespace umbraco.cs.fixer
{
    public partial class ProgramWindow : Form
    {
        public string FileNameWithPath { get; set; }
        public string SafeFileName { get; set; }
        public string FolderPath { get; set; }
        public string[] newFile { get; set; }

        public ProgramWindow()
        {
            InitializeComponent();
        }

        private void OpenMasterFileClick(object sender, EventArgs e)
        {
            // allow only master
            selectedFile.Filter = "Master files|*.master";

            if (selectedFile.ShowDialog() != DialogResult.OK)
            {
                AddLineToLog("You must select a file to parse!");
                return;
            }

            // Clear log
            txtLog.Text = string.Empty;

            // Set some properties
            FileNameWithPath = selectedFile.FileName;
            SafeFileName = selectedFile.SafeFileName;
            FolderPath = Path.GetDirectoryName(FileNameWithPath);
            newFile = File.ReadAllLines(FileNameWithPath);

            // Display some verification info about the file being processed
            AddLineToLog(string.Concat("You selected ", SafeFileName));

            // Verify the loaded masterfile
            if (!VerifyFile(newFile[0]))
            {
                return;
            }

            // Check for uSiteBuilder binaries
            if (chkDocumentTypes.Checked)
            {
                var documentTypes = USiteBilder(FolderPath);

                if (documentTypes.Any())
                {
                    // Move and show additional elements
                    btnCreate.Top = 75 + 55;

                    ddlDocumentType.Visible = true;
                    lblDocumentType.Visible = true;

                    ddlDocumentType.DataSource = documentTypes;
                    ddlDocumentType.DisplayMember = "FullName";
                    ddlDocumentType.ValueMember = "FullName";
                }
            }

            AddLineToLog("Your masterfile looks fine, please verify the settings and confirm");
            pnlSettings.Visible = true;
        }

        private List<DocumentTypes> USiteBilder(string folderPath)
        {
            var documentTypes = new List<DocumentTypes>();

            // We assume the root folder is below the masterpages directory
            var rootFolder = Directory.GetParent(folderPath).FullName;
            var binFolder = Path.Combine(rootFolder, "bin");
            if (Directory.Exists(binFolder))
            {
                var files = System.IO.Directory.GetFiles(binFolder).Where(x => x.Contains(".dll"));

                foreach (var asm in files.Select(Assembly.LoadFile))
                {
                    try
                    {
                        var types = asm.GetTypes();
                        documentTypes.AddRange(from type in types where TraverseForBaseType(type) select new DocumentTypes() { FullName = type.FullName, Name = type.Name });
                    }
                    catch (ReflectionTypeLoadException ex)
                    { }
                }
            }

            return documentTypes;
        }

        private class DocumentTypes
        {
            public string Name { get; set; }
            public string FullName { get; set; }
        }

        private bool TraverseForBaseType(Type type)
        {
            if (type == null || type.BaseType == null)
            {
                return false;
            }

            if (!string.IsNullOrEmpty(type.BaseType.FullName) && type.BaseType.FullName.Equals("Vega.USiteBuilder.DocumentTypeBase"))
            {
                return true;
            }
            else
            {
                bool parentType = TraverseForBaseType(type.BaseType);
                if (parentType)
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Add a line to the log
        /// </summary>
        /// <param name="text"></param>
        private void AddLineToLog(string text)
        {
            txtLog.Text = string.IsNullOrEmpty(txtLog.Text) ? text : string.Concat(txtLog.Text, Environment.NewLine, text);
        }

        /// <summary>
        /// Some verifications of the masterfile, may get more strict in the fuature
        /// </summary>
        /// <param name="firstLine"></param>
        /// <returns></returns>
        private bool VerifyFile(string firstLine)
        {
            AddLineToLog("Verifying file...");

            if (string.IsNullOrEmpty(firstLine))
            {
                AddLineToLog("Could not parse file, is the header missing or not the first line?");
                return false;
            }

            //string pattern = @"^(?=.*\bMaster\b)(?=.*Language=""C#"").+$";
            //var result = Regex.IsMatch(newFile[0], pattern) ? "Matched" : "Not matched";

            // Does the first line contain the "Master" word?
            if (!Regex.IsMatch(firstLine, @"\bMaster\b"))
            {
                AddLineToLog("Missing statement [Master]. Are you shure this is a .master file?");
                return false;
            }

            // Check for correct language
            if (!Regex.IsMatch(firstLine, "Language=\"C#\""))
            {
                AddLineToLog("Missing or bad attribute [Language].");
                return false;
            }

            // Verify that the file isn't already patched
            //CodeFile, CodeBehind, Inherits
            if (Regex.IsMatch(firstLine, @"(CodeFile|CodeBehind|Inherits)", RegexOptions.IgnoreCase))
            {
                AddLineToLog("File already seems patched.");
                AddLineToLog("Your master declaration should not contain any of these words");
                AddLineToLog("[CodeFile] [CodeBehind] [Inherits]");
                return false;
            }

            return true;
        }

        /// <summary>
        /// Create cs file, patch masterpage and possibly create a backup
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCreate_Click(object sender, EventArgs e)
        {
            // Aha, lets patch the line
            // Split at each separator
            var firstLineList = newFile[0].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).ToList();

            string namespaceName = string.IsNullOrEmpty(txtNamespace.Text) ? "masterpages" : Helpers.StringMethods.VariableSafeString(txtNamespace.Text);
            var className = StringMethods.VariableSafeString(SafeFileName);

            // Add the required attributes
            firstLineList.Remove("%>");
            firstLineList.Add(string.Format("CodeFile=\"{0}.cs\"", SafeFileName));
            firstLineList.Add(string.Format("Inherits=\"{0}.{1}\"", namespaceName, className));
            firstLineList.Add("%>");

            // Write the modififications to the first line
            newFile[0] = string.Join(" ", firstLineList.ToArray());

            if (!CreateAndPatchFiles(namespaceName, className))
            {
                pnlSettings.Visible = false;
            }
        }

        private bool CreateAndPatchFiles(string namespaceName, string className)
        {
            // Make backup of .master file
            if (chkBackup.Checked)
            {
                try
                {
                    string bakFilePath = string.Concat(FileNameWithPath, ".bak");
                    File.Copy(FileNameWithPath, bakFilePath);
                    AddLineToLog("------------------------------------");
                    AddLineToLog(string.Concat("Successfully created a backup file at ", bakFilePath));
                }
                catch (Exception ex)
                {
                    AddLineToLog("Could not create backup file, halting.");
                    AddLineToLog(ex.Message);
                    return false;
                }
            }

            // Create .cs file
            try
            {
                AddLineToLog("------------------------------------");

                string uSiteBuilderReference = string.Empty;
                string inheritFrom = "System.Web.UI.MasterPage";
                string miscInfo = string.Empty;

                if (ddlDocumentType.Visible && ddlDocumentType.SelectedValue != null)
                {
                    AddLineToLog("Adding reference to document type...");
                    DocumentTypes documentType = ddlDocumentType.SelectedItem as DocumentTypes;
                    if (documentType != null)
                    {
                        uSiteBuilderReference = "using Vega.USiteBuilder;";
                        inheritFrom = string.Format("Vega.USiteBuilder.TemplateBase<{0}>", documentType.FullName);
                        miscInfo = string.Concat(
                            "            // Time to use CurrentContent, for more information on usitebuilder please visit http://usitebuilder.vegaitsourcing.rs/ or http://usitebuilder.codeplex.com/"
                            , Environment.NewLine
                            , "            // this.CurrentContent"
                            );

                        AddLineToLog("Successfully added a reference to the document type " + documentType.FullName);
                    }
                }

                var csTemplate = string.Format(@"using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
{3}

namespace {0}
{{
    /// <summary>
    /// File generated with Umbraco MasterPage CodeFile Fixer, http://umbracocs.codeplex.com
    /// </summary>
    public partial class {1} : {2}
    {{
        protected void Page_Load(object sender, EventArgs e)
        {{
{4}
        }}
    }}
}}", namespaceName, className, inheritFrom, uSiteBuilderReference, miscInfo);

                string csFilePath = Path.Combine(FolderPath, string.Concat(SafeFileName, ".cs"));
                File.WriteAllText(csFilePath, csTemplate);
                AddLineToLog(string.Concat("Successfully created a new cs file at ", csFilePath));
            }
            catch (Exception ex)
            {
                AddLineToLog("Could not create .cs file, halting.");
                AddLineToLog(ex.Message);
                return false;
            }

            // Rewrite .master file
            try
            {
                File.WriteAllLines(FileNameWithPath, newFile);
                AddLineToLog("------------------------------------");
                AddLineToLog("Successfully patched the masterfile.");
            }
            catch (Exception ex)
            {
                AddLineToLog("Could not modify the masterfile, halting.");
                AddLineToLog(ex.Message);
                return false;
            }

            return true;
        }

        private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            System.Diagnostics.Process.Start("http://umbracocs.codeplex.com");
        }

        private void ddlDocumentType_Enter(object sender, EventArgs e)
        {
            var control = sender as ComboBox;
            control.DroppedDown = true;
        }

        private void ddlDocumentType_Leave(object sender, EventArgs e)
        {
            var control = sender as ComboBox;
            control.DroppedDown = false;
        }

        private void ddlDocumentType_DropDown(object sender, EventArgs e)
        {
            var control = sender as ComboBox;
            control.DropDownWidth = DropDownWidth(control) + 50;
        }

        private int DropDownWidth(ComboBox senderComboBox)
        {
            int width = senderComboBox.DropDownWidth;
            Graphics g = senderComboBox.CreateGraphics();
            Font font = senderComboBox.Font;

            int vertScrollBarWidth = (senderComboBox.Items.Count > senderComboBox.MaxDropDownItems)
                    ? SystemInformation.VerticalScrollBarWidth : 0;

            var itemsList = senderComboBox.Items.Cast<object>().Select(item => item.ToString());

            foreach (string s in itemsList)
            {
                int newWidth = (int)g.MeasureString(s, font).Width + vertScrollBarWidth;

                if (width < newWidth)
                {
                    width = newWidth;
                }
            }
            return width;
        }
    }
}
